/*
 *  TagFolder
 *      Tagging everything
 *
 *  Currently tag relationships are not supported
 */

#define FUSE_USE_VERSION 26

#include <fuse.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "tagfolder.h"
#include "tf_log.h"
#include "tf_list.h"

#if defined( __cplusplus)
extern "C"{
#endif

/**
 * Mount point which is specified when mount the filesystem;
 */
static char *mount_point=NULL;

/**
 * Clean up routine;
 *      relase unused resources
 */
static void tagfs_clean_up(void ){
    ENTER_FUNC;
    if( mount_point!=NULL){
        free( mount_point);
        mount_point=NULL;
    }
    LEAVE_FUNC;
}

/**
 *  Get the attributes of specific path;
 *  This is a VERY IMPORTANT FUNCTION!!!!!
 *      needs to be carefully crafted.
 *  parameters: path=> tag name, hiar
 *          buf=> result buffer
 *  return: 0=>OK, !0=>Error
 */
static int tagfs_getattr(const char *path, struct stat *buf){
    ENTER_FUNC;
    int res = 0, i=0;

    memset(buf, 0, sizeof(struct stat));
    if( strcmp(path, "/") == 0) {
        buf->st_mode = S_IFDIR | 0777;
    } else{
        string_list* tlist=NULL;
        string_list_tok( path, &tlist, "/");
        if( tlist!=NULL){
            if( if_tag_defined( get_from_string_list( tlist, 0))){
                buf->st_mode = S_IFDIR | 0777;
                LEAVE_FUNC;
                return 0;
            }else{
                errno = ENOTDIR;
                return -1;
            }
            free_string_list( tlist);
        }
    }
    LEAVE_FUNC;
    return res;
}

static int tagfs_mknod(const char *path, mode_t mod, dev_t dev){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}

/**
 * Mkdir in this file system means defining new tags;
 * parameters: path=>a series of tags, splitted by '/'
 *      mode=> dir mode, which is simply ignored;
 */
static int tagfs_mkdir(const char *path, mode_t mod){
    ENTER_FUNC;
    /*
     *if( path==NULL || strlen(path)==0){
     *    LEAVE_FUNC;
     *    errno = EFAULT;
     *    return -1;
     *}
     */
    string_list *tlist=NULL;
    string_list_tok( path, &tlist, "/");

    if( tlist!=NULL && tlist->length==1){
        define_tag( get_from_string_list( tlist, 0));
        free_string_list( tlist);
        LEAVE_FUNC;
        return 0;
    }else{
        LEAVE_FUNC;
        return -1;
    }
}

static int tagfs_unlink(const char *path){
    ENTER_FUNC;
    //Todo
    LEAVE_FUNC;
    return 0;
}

static int tagfs_rmdir(const char *path){
    ENTER_FUNC;
    string_list* tlist=NULL;
    int i=0, ret=0;

    if(path!=NULL){
        string_list_tok( path, &tlist, "/");
        if( tlist!=NULL && tlist->length==0){
            if( if_tag_defined( get_from_string_list( tlist, 0)) !=0){
                undefine_tag( get_from_string_list( tlist, 0));
            }else{
                ret=-1;
            }
            free_string_list( tlist);
        }
    }

    LEAVE_FUNC;
    return ret;
}
static int tagfs_rename(const char *path, const char *mod){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_link(const char *hlink, const char *path){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_chmod(const char *path, mode_t mod){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_chown(const char *path, uid_t uid, gid_t gid){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_truncate(const char *path, off_t offset){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_utime(const char *path, struct utimbuf *time){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_open(const char *path, struct fuse_file_info* fi){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info* fi){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_statfs(const char *path, struct statvfs *st){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_flush(const char *path, struct fuse_file_info *fi){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_release(const char *path, struct fuse_file_info *fi){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_fsync(const char *path, int x, struct fuse_file_info *fi){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}

/**
 * Open a dir for op
 * parameters: path=> dir to open;
 *      fi=>file info
 * return: 0=>OK, !0=>Error
 */
static int tagfs_opendir(const char *path, struct fuse_file_info *fi){
    ENTER_FUNC;
    if( path==NULL || fi==NULL || strlen(path)==0){
        return -1;
    }

    LEAVE_FUNC;
    return 0;
}

/**
 * Read a dir
 * parameters: path=> relative path of target dir
 *      buf=> result buf, will be filled by filler
 *      fdir=> function pointer to fill buf with data here read
 *      offset=> 
 *      fi=> 
 * return: 0=>OK, !0=>Error
 */
static int tagfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi){
    ENTER_FUNC;

    string_list* tags=NULL;
    int i=0;
    if( path==NULL){
        LEAVE_FUNC;
        return 0;
    }

    //Set up default file parameters for tags and 
    //symbolic links which is the tagged file;
    struct stat default_stat;
    default_stat.st_uid = getuid();
    default_stat.st_gid = getgid();

    filler( buf, ".", NULL, 0);
    filler( buf, "..", NULL, 0);

    if( strcmp( path, "/")==0){
        //Listing the root of TagFs;
        list_all_tags( &tags);

        if( tags!=NULL ){
            default_stat.st_mode = S_IFDIR | 0755;
            default_stat.st_nlink = 2;
            for( i=0; i<tags->length; ++i){
                const char *e=get_from_string_list( tags, i);
                if( e!=NULL){
                    filler( buf, e, &default_stat, 0);
                }
            }
        }

        free_string_list( tags);
        tags=NULL;
    }else{
        // Listing the none root of TagFs; 
        string_list* tlist=NULL;
        string_list_tok( path, &tlist, "/");
        if( tlist!=NULL){
            free_string_list( tlist);
        }
    }

    LEAVE_FUNC;
    return 0;
}
static int tagfs_releasedir(const char *path, struct fuse_file_info *fi){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
int tagfs_fsyncdir(const char *path, int x, struct fuse_file_info *fi){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static void* tagfs_init(struct fuse_conn_info *conn){
    ENTER_FUNC;


    LEAVE_FUNC;
    return NULL;
}
static void tagfs_destroy(void *x){
    ENTER_FUNC;
    if( mount_point!=NULL){
        free( mount_point);
        mount_point=NULL;
    }
    LEAVE_FUNC;
}
static int tagfs_access(const char *x, int x2){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_create(const char *path, mode_t mod, struct fuse_file_info *fi){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_ftruncate(const char *path, off_t offset, struct fuse_file_info *fi){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_fgetattr(const char *path, struct stat *st, struct fuse_file_info *fi){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_lock(const char *path, struct fuse_file_info *fi, int cmd, struct flock *fl){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_utimens(const char *path, const struct timespec tv[2]){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_bmap(const char *path, size_t blocksize, uint64_t *idx){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_ioctl(const char *path, int cmd, void *arg, struct fuse_file_info *fi, unsigned int flags, void *data){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}
static int tagfs_poll(const char *path, struct fuse_file_info *fi, struct fuse_pollhandle *ph, unsigned *reventsp){
    ENTER_FUNC;
    LEAVE_FUNC;
    return 0;
}

/**
 * Struct to provide tagfs fuse operations
 */
static struct fuse_operations tagfs_operations;

/**
 * Entry point
 */
int main( int argc, char** argv){
    ENTER_FUNC;

    tf_log( "pid:%d\n", (int) getpid());
    tf_init( argc, argv);
    memset( &tagfs_operations, sizeof( tagfs_operations), 0);

    // setup operation function pointers;
    tagfs_operations.getattr    = tagfs_getattr;
    tagfs_operations.mkdir      = tagfs_mkdir;
    tagfs_operations.rmdir      = tagfs_rmdir;
    tagfs_operations.init       = tagfs_init;
    tagfs_operations.destroy    = tagfs_destroy;
    tagfs_operations.opendir    = tagfs_opendir;
    tagfs_operations.readdir    = tagfs_readdir;
    tagfs_operations.open       = tagfs_open;
    tagfs_operations.read       = tagfs_read;
    /*
     *    tagfs_operations.readlink   = NULL;
     *    tagfs_operations.mknod      = NULL;
     *    tagfs_operations.unlink     = NULL;
     *    tagfs_operations.symlink    = NULL;
     *    tagfs_operations.rename     = NULL;
     *    tagfs_operations.link       = NULL;
     *    tagfs_operations.chmod      = NULL;
     *    tagfs_operations.chown      = NULL;
     *    tagfs_operations.truncate   = NULL;
     *    tagfs_operations.utime      = NULL;
     *    tagfs_operations.write      = NULL;
     *    tagfs_operations.statfs     = NULL;
     *    tagfs_operations.flush      = NULL;
     *    tagfs_operations.release    = NULL;
     *    tagfs_operations.fsync      = NULL;
     *    tagfs_operations.setxattr   = NULL;
     *    tagfs_operations.getxattr   = NULL;
     *    tagfs_operations.listxattr  = NULL;
     *    tagfs_operations.removexattr = NULL;
     *    tagfs_operations.releasedir = NULL;
     *    tagfs_operations.fsyncdir   = NULL;
     *    tagfs_operations.access     = NULL;
     *    tagfs_operations.create     = NULL;
     *    tagfs_operations.ftruncate  = NULL;
     *    tagfs_operations.fgetattr   = NULL;
     *    tagfs_operations.lock       = NULL;
     *    tagfs_operations.utimens    = NULL;
     *    tagfs_operations.bmap       = NULL;
     *    tagfs_operations.ioctl      = NULL;
     *    tagfs_operations.poll       = NULL;
     *
     */

    if( argc>1 && argv!=NULL && argv[1]!=NULL && mount_point==NULL){
        mount_point=(char*) malloc (strlen(argv[1])+1);
        strcpy( mount_point, argv[1]);
    }

    int ret=fuse_main( argc, argv, &tagfs_operations, NULL);
    tf_destroy();
    LEAVE_FUNC;
    return ret;
}

#if defined( __cplusplus)
} // extern "C"{
#endif
